/*
 * Created on 28/05/2004
 * 
 */
package appman;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Serializable;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.util.Random;
import java.util.Vector;
// import org.isam.exehda.Exehda;
import org.isam.exehda.ApplicationId;
import org.isam.exehda.ObjectId;
// import org.isam.exehda.services.Executor;


import appman.clustering.ClusteringPhase;
import appman.clustering.DAG_DSC;
import appman.parser.ApplicationDescription;
import appman.parser.SimpleParser;

import edu.berkeley.guir.prefuse.Display;
import edu.berkeley.guir.prefusex.force.DragForce;
import edu.berkeley.guir.prefusex.force.ForceSimulator;
import edu.berkeley.guir.prefusex.force.NBodyForce;
import edu.berkeley.guir.prefusex.force.SpringForce;

/**
 * @author lucasa@gmail.com
 *
 */
public class ApplicationManager implements Runnable, ApplicationManagerRemote, Serializable
{
	private String appmanId; // id
    
        /** List of available SMs */
	private Vector submissionmanagerList;
        /** Seed for creating new unique IDs for instantiated SMs */
	private int submanId = 0;
        /** helper counter used to implement round-robin scheduling over available SMs */
	private int schedule_loop = 0;
        /** Means: choosing any of the available SMs would be ok */   
    private static final String SCHED_ANY_SM = "";

        /** List of graphs already scheduled to some SM. */
	private Vector graphs;
        /** List of graphs yet to be scheduled tom some SM */
	private Vector newgraphs;
    
        /** Flags whether to start sending graphs to remotes machines(SubmissionManagers) */
	private boolean runsystem;
	
	static final int ApplicationManager_FINAL = 2;
	static final int ApplicationManager_READY = 0;
	static final int ApplicationManager_EXECUTING = 1;

        /** Current status of the execution, either READY, EXECUTING or FINAL */
	private int state = ApplicationManager_READY;

        //
        // execution timming stats
        //
	private long time_execution = 0;
	private long time_begin = 0;
	private long time_schedule_begin=0;
	private long time_schedule_end=0;
	private long time_schedule_total=0;
	private long time_schedule_begin2=0;
	private long time_schedule_end2=0;
	private long time_schedule_total2=0;
    //VDN
    private ApplicationDescription appDescription;
	protected ClusteringPhase cp;
	private long downloadTimeOfSM = 0;

        //
        // EXEHDA stuff
        //
        /** ID of the appman application running in EXEHDA */
	ApplicationId applicationId = null; 
        /** URL used to obtain a remote reference to this object */
	String my_contact_address = null;
	
	
	public ApplicationManager(String id)
	{
		appmanId = new String(id);
		submissionmanagerList = new Vector();
		
		graphs = new Vector();
		newgraphs = new Vector();
		
		runsystem = false;
		
		state = ApplicationManager_READY;
		
		applicationId = AppManUtil.getExecutor().currentApplication();
		
		//Thread thread = new Thread(this);
		//thread.start();
		Debug.newDebugFile("APPMANLOG", "appman.log");
		Debug.debug("ApplicationManager created.", true);		
		
				
	}
	public ApplicationManager(String id, ApplicationId appid)
	{		
		appmanId = new String(id);
		submissionmanagerList = new Vector();
		
		graphs = new Vector();
		newgraphs = new Vector();
		
		runsystem = false;
		
		state = ApplicationManager_READY;
		
		applicationId = appid;
		
		//Thread thread = new Thread(this);
		//thread.start();
		Debug.newDebugFile("APPMANLOG", "appman.log");
		Debug.debug("ApplicationManager created.", true);		
	}
	public ApplicationManager()
	{		
		appmanId = new String("Default");
		submissionmanagerList = new Vector();
	
		graphs = new Vector();
		newgraphs = new Vector();
	
		runsystem = false;
	
		state = ApplicationManager_READY;		
	
		Thread thread = new Thread(this);
		thread.start();
	
		Debug.debug("ApplicationManager Default created.", true);		
	}
	public synchronized String getInfoRemote() throws RemoteException
	{
		return getInfo();
	}
	public void PrintInfo() throws RemoteException
	{
		Debug.debug(getInfo());
	}
	public String getInfo() throws RemoteException
	{		
				String str = "";
				str+="\n------------ Application Manager ["+appmanId+"]-------------.";
				str+="\nApplication Manager execution status: " + getApplicationStatePercentCompleted()*100 + " %";
				for(int i=0;i < submissionmanagerList.size(); i++)
				{	
					SubmissionManagerRemote smr = (SubmissionManagerRemote)submissionmanagerList.elementAt(i);
					str+="\nApplicationManager manage Submission Manager: " + smr.getSubmissionManagerIdRemote();							
				}
				for(int j=0;j < graphs.size(); j++)
				{
					Graph g = (Graph)graphs.elementAt(j);
					str += "\nApplicationManager execute Graph ["+g.getGraphId()+"] by Submission Manager [" + g.getSubmissionManagerId()+"]";
					str += "\nGraph ["+g.getGraphId()+"] execution status: " + g.getStatePercentCompleted();
				}
				str+= "\n-------------------------";
				return str;		
	}
	
	public void startApplicationManagerRemote() throws RemoteException
	{
		startApplicationManager();
	}	
	public void startApplicationManager()
	{
		time_execution = System.currentTimeMillis();
		time_begin = System.currentTimeMillis();
		runsystem = true;
		state = ApplicationManager_EXECUTING;
	}
	
	public void addGraphRemote(Graph g) throws RemoteException
	{
		addGraph(g);
	}
	
	public void addGraph(Graph g)
	{		
		synchronized(newgraphs)
		{		
			newgraphs.add(g);
		}
		Debug.debug("ApplicationManager add a new graph: " + g.getGraphId(), true);
	}
		
	public float getApplicationStatePercentCompletedRemote() throws RemoteException
	{
		return getApplicationStatePercentCompleted();
	}
	public int getApplicationStateRemote() throws RemoteException
	{
		return getApplicationState();
	}
	public int getApplicationState()
	{
		return state;
	}
	public float getApplicationStatePercentCompleted()
	{		
		synchronized(graphs)
		{
			int n = graphs.size();
			float sum = 0;
			for(int i=0;i < graphs.size(); i++)
			{				
					sum+= ((Graph)graphs.elementAt(i)).getStatePercentCompleted();
			}
			if(n > 0)			
				return sum/n;
			else
				return 0;
		}
	}


    
        /**
         * Returns the graph with ID <code>graphId</code> in the graphs or newgraphs
         * array.
         *
         * @param graphId a <code>String</code> value
         * @return a <code>Graph</code> value
         */
    public Graph getGraph(String graphId)
	{
			for(int i=0;i < graphs.size(); i++)
			{
				if(((Graph)graphs.elementAt(i)).getGraphId().compareTo(graphId) == 0)
				{
					return (Graph)graphs.elementAt(i);
				}
			}

			for(int i=0;i < newgraphs.size(); i++)
			{
				if(((Graph)newgraphs.elementAt(i)).getGraphId().compareTo(graphId) == 0)
				{
					return (Graph)newgraphs.elementAt(i);
				}
			}
		
		return null;
	}
	
	public synchronized boolean isTaskOutputsRemoteAvailable(String taskId) throws RemoteException,java.net.SocketException
	{		
				//Debug.debug("ApplicationManager isDisponibleTaskOutputsRemote locating task ["+taskId+"] in "+graphs.size()+" graphs");
				for(int i=0;i < graphs.size(); i++)
				{
					Graph g = (Graph)graphs.elementAt(i);
					Task t = g.getTask(taskId);
					//Debug.debug("ApplicationManager isDisponibleTaskOutputsRemote looking for task ["+taskId+"] in graph ["+g.getGraphId()+"]");
					if( t  != null )
					{						
						if(t.getTaskState() == Task.TASK_FINAL)
						{
							Debug.debug("ApplicationManager isTaskOutputsRemoteAvailable task ["+t.getTaskId()+"] status: " + t.getTaskStateString());
							return true;
						}
					}
				}		
		return false;
	}
	/*
	 * Retorna uma refer�ncia remota do servi�o de transfer�ncia de arquivos de uma tarefa
	 */
	public String getTaskGridFileServiceContactAddressRemote(String taskId) throws RemoteException
	{		
        for(int i=0;i < graphs.size(); i++)
        {
            Graph local = (Graph)graphs.elementAt(i);
            Task t = local.getTask(taskId);
            if( t  != null )
            {
                try
                {
                    SubmissionManagerRemote sm = getSubmissionManagerRemote(local.getSubmissionManagerId());
                    String gfsr = sm.getMyObjectRemoteContactAddress();
                    return gfsr;
                }catch (RemoteException e)
                {			
                    Debug.debug(e);
                    e.printStackTrace();
                    throw e;
                }				
            }
        }
		return null;
	}
	
	public SubmissionManagerRemote getSubmissionManagerRemote(String subId) throws RemoteException
	{
        synchronized (submissionmanagerList) {
            for(int i=0;i < submissionmanagerList.size(); i++)
            {
                SubmissionManagerRemote candidate = (SubmissionManagerRemote)
                    submissionmanagerList.elementAt(i);
                if ( candidate.getSubmissionManagerIdRemote().equals(subId) ) {
                    return candidate;
                }
            }
        }
		return null;
	}
    

	

	private SubmissionManagerRemote scheduleSubmissionManager(String subId) 
	{


		SubmissionManagerRemote subr = null;

        synchronized (submissionmanagerList) {

            try
            {
                    // either return the specific SM requested or do a round-robin selection
                    // among the SMs available
                if ( SCHED_ANY_SM.equals(subId) ) { // round-robin
                    if ( submissionmanagerList.isEmpty() ) {
                        subId = String.valueOf(submanId++);
                        Debug.debug("ApplicationManager need to create a new SubmissionManager: " + subId, true);
                        subr = createNewSubmissionManager(subId);
                        submissionmanagerList.add(subr);
                    }
                    else {
                        subr = (SubmissionManagerRemote)
                            submissionmanagerList.elementAt(
                                schedule_loop++ % submissionmanagerList.size());
                    }
                }
                else { // a specific SM instance has been requested
                    subr = getSubmissionManagerRemote(subId);
                    if ( subr == null ) {
                        Debug.debug("ApplicationManager need to create a new SubmissionManager: " + subId, true);
                        subr = createNewSubmissionManager(subId);
                        submissionmanagerList.add(subr);
                    }
                }
			
                Debug.debug("ApplicationManager scheduling a SubmissionManager", true);
                subr.getIsAliveRemote();
                Debug.debug("ApplicationManager scheduled the SubmissionManager ["+subr.getSubmissionManagerIdRemote()+"]", true);
							
            }
            catch (RemoteException e) {
                    // Tolerancia a Falhas
                    // se o Submission Manager remoto escolhido nao responder ao ping isAlive, entao 
                    // remove este da lista e  recursivamente realiza outro escalonamento
                    //
                Debug.debug("Tolerancia a Falhas - " + e);
                Debug.debug("ApplicationManager Scheduling SubmissionManager ERROR, removing fault SubmissionManager from the list");
                submissionmanagerList.removeElement(subr);			
                subr =  scheduleSubmissionManager();
            }

            System.gc();
				
            return subr;
        }
	}
	

	public void setMyObjectContactAddress(String contact)
	{
		my_contact_address = contact;
	}
	public void setMyObjectContactAddressRemote(String contact) throws RemoteException
	{
		setMyObjectContactAddress(contact);
	}

    


    
	public synchronized void addApplicationDescriptionRemote(byte[] filedata) throws RemoteException
	{
		try
		{
			addApplicationDescription(filedata);
		}catch (Exception e)
		{
			throw new RemoteException(e.toString());
		}
	}
	
	
	public void addApplicationDescription(byte[] filedata) throws Exception
	{
		GridFileService fileservice = new GridFileService(this.appmanId);
		String name = "graph.dag";
		fileservice.uploadFile(filedata, name);
		File file = fileservice.getFile(name);
		String[] args = new String[1];
		args[0] = file.getAbsolutePath();
		ApplicationDescription appdesc = null;
		try
		{			
			appdesc = SimpleParser.parseGRIDADL(args);
		} catch (Exception e)
		{
			Debug.debug("Error on parser: " + e);			
			Debug.debug(e);
			e.printStackTrace();
			throw new Exception("Error on parser GRID-DAG file: " + e);
		}
		
		//VDN: particiona
		//cp = new ClusteringPhase(appdesc.getDAG());
		cp = ((DAG_DSC)appdesc.getDAG()).getCP();
		 
		//Vector clusterP = cp.clustering();
		Vector clusterP = cp.getCluster();
		//Vector clusterP = SimpleParser.cp.getCluster();
		//Vector clusterP = ((DAG_DSC)appdesc.getDAG()).clustering();
		//cp = ((DAG_DSC)appdesc.getDAG()).getCP();
		int nclusters = cp.getNumberOfLevels();
		//int nclusters = SimpleParser.cp.getNumberOfLevels();
		
		String[] clusters = new String[nclusters];
		for(int i=0; i<nclusters; i++)
		{				
			clusters[i] = "cluster"+String.valueOf(i);
		}		
		
		Random rand  = new Random();
		//GraphGenerator.clusteringAlgorithm(clusters, appdesc); //VDN Comentou
		//VDN: defino o numero de clusters depois do particionamento. 
		GraphGenerator.clusteringPhaseAlgorithm(clusterP, clusters, appdesc);//VDN Inseriu
		System.out.println("\t\t\n\nVDN: "+clusters.length+"\n");
		nclusters = clusters.length; //VDN
		System.out.println("\t\t\n\nVDN: "+nclusters+"\n");
		String graph_name[] = new String[nclusters];
		for(int j=0;j<nclusters;j++)
		{
			graph_name[j] = "grafo"+String.valueOf(rand.nextInt());												
			addApplicationDescription(graph_name[j], clusters[j], appdesc);
		}
	}
	public synchronized void addApplicationDescriptionRemote(String graphId, String clusterId, ApplicationDescription appdesc) throws RemoteException
	{
		addApplicationDescription(graphId, clusterId, appdesc);
	}
	public void addApplicationDescription(String graphId, String clusterId, ApplicationDescription appdesc)
	{
		Debug.debug("ApplicationManager add a new ApplicationDescription", true);		
		// cria um grafo default rand�mico
		Graph g= new Graph(graphId, clusterId, appdesc);
		addGraph(g);
	}
	
//	VDN:27/1/2006
	public long calculateDownloadTimeNow() throws RemoteException
	{
		  long plus = 0;
		  
		  for(int i=0; i<submissionmanagerList.size(); i++)
		  {
		  		plus += ((SubmissionManagerRemote)submissionmanagerList.elementAt(i)).getDownloadTimeOfTasksManagers();
		  }

		  downloadTimeOfSM = plus;
		  
		  return plus;
	}

//	VDN:27/1/2006
	private void setDownloadTimeOfSM( long downloadTime )
	{
		downloadTimeOfSM = downloadTime;
	}

//	VDN:27/1/2006
	public long getDownloadTimeOfSM( ) throws RemoteException
	{
		return downloadTimeOfSM;	
	}	
	
	public void setAllSubmissionManagersToDie()
	{
		System.out.println("[AM] SET ALL AM TO DIE: ");
		Debug.debug("[AM] SET ALL AM TO DIE: ", true);

		long plus = 0;
		for(int i=0;i < submissionmanagerList.size(); i++)
		{
			try
			{
				((SubmissionManagerRemote)submissionmanagerList.elementAt(i)).setDieRemote();
				plus += ((SubmissionManagerRemote)submissionmanagerList.elementAt(i)).getDownloadTimeOfTasksManagers();
				Debug.debug("[AM "+i+"] TIME DOWNLOAD: "+((SubmissionManagerRemote)submissionmanagerList.elementAt(i)).getDownloadTimeOfTasksManagers(), true);
				//System.out.println("[AM] Download Time: "+plus);
				//Debug.debug("[AM] Download Time: "+plus, true);
			} catch (RemoteException e) {
				
			}
		}
		
		setDownloadTimeOfSM( plus );
	}
	public void computeApplicationExecutionTimes()
	{
		String file_path = "tasks-execution-"+this.appmanId+".trace"; 
		Debug.newDebugFile("TASK\tERROR-RETRY-TIMES\tCREATED-TIME\tSUBMITED-TIME\tSTARTED_TIME\tFINISHED_TIME\tEXECUTION-TIME", file_path);				
		for(int i=0;i < graphs.size(); i++)
        {
            Graph g = (Graph)graphs.elementAt(i);
            Vector tasks = g.getTaskList();
            Debug.debug("ApplicationManager begin time: " + time_begin, true);
            for(int j=0; j<tasks.size(); j++)
            {
            	Task t = (Task)tasks.elementAt(j);
            	long time = t.getTimeEnd() - t.getTimeStart();
            	Debug.debug("ApplicationManager task "+t.getTaskId()+" submit time: " + t.getTimeSubmit(), true);

            	Debug.debugToFile("\n"+t.getTaskId() +
                                  "\t"+t.getRetryTimes()+
                                  "\t"+ (t.getTimeTaskCreated()) +
                                  "\t"+ (t.getTimeSubmit()) +
                                  "\t"+ (t.getTimeTaskStart())+
                                  "\t"+ (t.getTimeTaskEnd()) +
                                  "\t"+(float)(time)+
                                  "\t"+(float)(time/1000),
                                  file_path, true);
                
            	Debug.debugToFile(t.getMySubmissionManagerRemoteContactAddress()+"\n",file_path, true);
            }
        }
	}
    
        /**
         * Main-thread. Stays in loop (2 steps) until the computation is completed:
         *
         * <ol>
         *
         * <li> First, it consumes graphs from the newgraphs queue, scheduling
         * them to same of the available SMs.
         *
         * <li> Second, updates the local mirror structure of already scheduled graphs by
         * contacting the corresponding SM.
         *
         * </ol>
         */
    public void run()
	{
		Debug.debug("ApplicationManager thread run.");
		float percent_completed = 0;
		
		while(this.getApplicationStatePercentCompleted() < 1)
		{
			if(runsystem == true)
			{
                    // schedule graphs pending in the newgraphs queue
                schedulePendingGraphs();
                    
				//try
				//{

                
					//Debug.debug("ApplicationManager run update "+graphs.size()+" graphs remote data.");	
					// atualiza os dados dos grafos, baixando o grafo atualizado do submission manager remoto
							if(graphs.size() > 0)
							{
									int i = 0;
									while(i < graphs.size())
									{
										Graph local = (Graph)graphs.elementAt(i);
										i++;							
										SubmissionManagerRemote subman = null;
										Graph remote = null;
										if(local.getStatePercentCompleted() < 1)
										{
												try
												{									
													subman = this.getSubmissionManagerRemote(local.getSubmissionManagerId());
													Debug.debug("ApplicationManager contacting SubmissionManager ["+subman.getSubmissionManagerIdRemote()
                                                                +"] to update remote graph: " + local.getGraphId(), true);
													//subman.PrintInfoRemote();											
													remote = subman.getGraphRemote(local.getGraphId());
												} catch (RemoteException e)
												{
													// Toler�ncia a Falhas
													// Se o Submission Manager n�o responder ent�o
													// remove o grafo da lista
													// adiciona o grafo novamente no Application Manager com um novo SubMan escalonado									
													Debug.debug("Toler�ncia a Falhas - " + e, true);
													e.printStackTrace();
													//System.exit(0);
													try
													{
														//time_schedule_begin2 = System.currentTimeMillis();
														subman = scheduleSubmissionManager();
														//time_schedule_end2 = System.currentTimeMillis();
														//time_schedule_total2 =time_schedule_total2 + (time_schedule_end2-time_schedule_begin2);

														local.setSubmissionManagerId(subman.getSubmissionManagerIdRemote());
														graphs.remove(local);												
														this.addGraph(local);							
													} catch (Exception e2)
													{
                                                        AppManUtil.exitApplication("Toler�ncia a Falhas: ERRO FATAL N�O TOLERADO", e2);
// 														Debug.debug("Toler�ncia a Falhas: ERRO FATAL N�O TOLERADO!");
// 														Debug.debug(e2, true);
// 														e2.printStackTrace();
// 														//System.exit(0);
// 														((Executor) Exehda.getService("executor")).exitApplication();
													}
												}

                                                    // update local graph copy with the new state grabbed from the remote SM
                                                if ( remote != null ) {
                                                    local.copy(remote);

                                                    __debug__("local graph ["+local.getGraphId()+"] UPDATED, "
                                                              +"percent completed="+local.getStatePercentCompleted());
                                                }
										
// 												if(remote != null)
// 												{
                                                    
// 													try
// 													{
// 														Debug.debug("ApplicationManager got graph "+remote.getGraphId()+" remote data.");
// 														if( subman.getGraphStatePercentCompletedRemote(local.getGraphId()) != local.getStatePercentCompleted() )
// 														{
// // 															Debug.debug("ApplicationManager update graph["+remote.getGraphId()+"] data: remote "
// //                                                                         + String.valueOf(subman.getGraphStatePercentCompletedRemote(remote.getGraphId()) ) );
                                                            
// 															Debug.debug("ApplicationManager update graph ["+local.getGraphId()+"] data: local "
//                                                                         + String.valueOf(local.getStatePercentCompleted()), true);
                                                            
// 															updateGraph(local.getGraphId(), remote);
// 														}										
// 													}
// 													catch (Exception e)
// 													{
//                                                         AppManUtil.exitApplication("Toler�ncia a Falhas: ERRO FATAL N�O TOLERADO", e);
// // 														Debug.debug("Toler�ncia a Falhas: ERRO FATAL N�O TOLERADO!", true);
// // 														Debug.debug(e, true);
// // 														e.printStackTrace();
// // 														//System.exit(0);
// // 														((Executor) Exehda.getService("executor")).exitApplication();
// 													}									
// 												}
										}// fim if percent completed
									}// fim while
							}// fim if graph size
		
			} // end if
			
			if(percent_completed < getApplicationStatePercentCompleted())
			{
				percent_completed = getApplicationStatePercentCompleted();
				Debug.debug("ApplicationManager Application graphs completed: " + getApplicationStatePercentCompleted(), true);				
			}
			
			try
			{		
				Thread.sleep(5000);
			} catch (Exception e)
			{
				Debug.debug(e, true);
				e.printStackTrace();				
			}
			
		} // end while
		
		state = ApplicationManager_FINAL;

		computeApplicationExecutionTimes();
//		Debug.debug("ApplicationManager cleaning Application Files! ", true);
		Debug.debug("ApplicationManager set all Submission Managers TO DIE! ", true);
		setAllSubmissionManagersToDie();
        
		
		//VDN
		time_execution = System.currentTimeMillis() - time_execution;
		Debug.debug("ApplicationManager Application completed time: " + (float)time_execution/1000 + " seconds", true);
        appDescription = appman.parser.SimpleParser.appDescription;
        FileWriter parserOut;
        
        try{
    			parserOut = new FileWriter("parseOut.txt",true);
    			parserOut.write("Scheduler Time(ms): "+((float)time_schedule_total)+"\n");
    			//parserOut.write("Scheduler Time2(ms): "+((float)time_schedule_total2)+"\n");
    			parserOut.write("Download Time(s): "+((long)getDownloadTimeOfSM()/1000)+"\n");
    			parserOut.write("Total Time(s): "+((float)time_execution/1000)+"\n");
    			parserOut.write("Total Memory: "+Runtime.getRuntime().totalMemory()+"\n");
    			parserOut.write("Free Memory: "+Runtime.getRuntime().freeMemory()+"\n");
    			parserOut.write("Max Memory: "+Runtime.getRuntime().maxMemory()+"\n");
    			parserOut.close();
                //appDescription.grappaOut.close(); VDN:4/1/06
             
        }catch( IOException e){
                System.out.println("[APPMAN-ApplicationManager.java]: "+e);
        }


//         AppManUtil.exitApplication();
// 		((Executor) Exehda.getService(Executor.SERVICE_NAME)).exitApplication();
	}// end run
	
	//VDN
	public ApplicationDescription getApplicationDescription() throws RemoteException{
		appDescription = appman.parser.SimpleParser.appDescription;
		return appDescription;
	}
	
	//VDN
	public void teste() throws RemoteException{
		System.out.print("\n\n\nTESTE APPLICATION MANAGER\n\n");
	}
	
	
	public Display startAppGUIRemote(String graphId) throws RemoteException
	{
		return startAppGUI(graphId);
	}
	
	public Display startAppGUI(String graphId)
	{
		Debug.debug("ApplicatinManager GUI Interface loading...",  true);
		while(this.getGraph(graphId) == null)
		{
			try
			{		
				Thread.sleep(100);
			} catch (Exception e)
			{
				Debug.debug(e, true);
				e.printStackTrace();				
			}
		}
		
			Graph g = this.getGraph(graphId);						
				
			Debug.debug("ApplicatinManager GUI Interface creating display for graph: " + g.PrintInfo(), true);
		
			ForceSimulator fsim = new ForceSimulator();
			fsim.addForce(new NBodyForce(-0.4f, -1f, 0.9f));
			fsim.addForce(new SpringForce(4E-5f, 75f));
			fsim.addForce(new DragForce(-0.005f));
	        			
			ForceDemo fdemo = new ForceDemo(g.getGraph(), fsim);			
			fdemo.runDemo();
					
			return null;
	}
	public Display startDefaultAppGUI()
	{
			Debug.debug("ApplicatinManager Default GUI Interface loading...",  true);
				
			ForceSimulator fsim = new ForceSimulator();
			fsim.addForce(new NBodyForce(-0.4f, -1f, 0.9f));
			fsim.addForce(new SpringForce(4E-5f, 75f));
			fsim.addForce(new DragForce(-0.005f));	        							
		
			GraphGenerator generator = new GraphGenerator("");
			edu.berkeley.guir.prefuse.graph.Graph gt = generator.getRandomTreeDirected(3, 3, (float)0.7);
			ForceDemo fdemo = new ForceDemo(gt, fsim);
			Debug.debug("ApplicatinManager GUI Interface creating display for DEFAULT graph: "+gt.getNodeCount()+" nodes, "+gt.getEdgeCount()+" edges", true);
		
			fdemo.runDemo();
				
			return null;
	}

	public static void main(String[] args) throws Exception
	{
		Debug.debug("This program is going to test the AppMan class");
		ApplicationManager appman = new ApplicationManager("teste");		
	}

        ////////////////////////////////////////////////////////////////
        // refactoring
        ////////////////////////////////////////////////////////////////

    private final void __debug__(String msg)
        {
            Debug.debug("[APPMAN] "+msg, true);
        }
    
        /**
         * Assigns graphs yet to scheduled to some available SM.
         *
         */
    private final void schedulePendingGraphs()
        {
                // TODO: limit the number of scheduling to 2x the number of pending
                // graphs to not lock forever
            
                // while there are graphs to be scheduled
            while ( ! newgraphs.isEmpty() ) {
                SubmissionManagerRemote subman = null;
                    // schedule the next pending graph
                synchronized (graphs) {
                    Graph ng = (Graph) newgraphs.remove(0);

                    try {
                            // select a candidate SM
                        time_schedule_begin = System.currentTimeMillis();
                        subman = scheduleSubmissionManager(ng.getGraphId());
                        time_schedule_end = System.currentTimeMillis();
                        time_schedule_total=time_schedule_total + (time_schedule_end-time_schedule_begin);

                            // assing the graph to the SM
                        try {
                            String smId = subman.getSubmissionManagerIdRemote();
                            ng.setSubmissionManagerId(smId);
                            subman.addGraphRemote(ng);
                            graphs.add(ng);

                            __debug__("graph["+ng.getGraphId()+"] scheduled to SM["+smId+"]");
                        }
                        catch (RemoteException re) {
                            __debug__("Failed to assign graph to SM="+subman
                                      +". Graph will be re-scheduled");
                                // return the graph to the pending graphs pool
                            ng.setSubmissionManagerId(null);
                            newgraphs.add(ng);
                        }
                    }
                    catch (Exception e) {
                        AppManUtil.exitApplication("FATAL ERROR while scheduling graphs to SMs", e);
                    }                    
                }
            }
        }
    
        /**
         * Select a SM through a RR scheduling over the available SMs.
         *
         * @return a <code>SubmissionManagerRemote</code> value
         */
    private final SubmissionManagerRemote scheduleSubmissionManager()
	{
		return scheduleSubmissionManager(SCHED_ANY_SM);
	}
    
        /**
         * Instantiate a new SM with the given ID, returning a reference to it.
         *
         * @param subId a <code>String</code> value
         * @return a <code>SubmissionManagerRemote</code> value
         * @exception RemoteException if an error occurs
         */
    private final SubmissionManagerRemote createNewSubmissionManager(String subId) throws RemoteException
	{
        SubmissionManagerRemote sub = null;
        try {
            Debug.debug("ApplicationManager creating new remote SubmissionManager: " + subId, true);
                    
            sub = exehdaCreateNewSubmissionManager(subId);
        }
        catch (Exception e2) {
            AppManUtil.exitApplication(null, e2);
        }
        finally {
            Debug.debug("DEBUG: "  + sub, true);
            
            if(sub == null) {
                throw new RemoteException("Failed to instantiate new Submission Manager");
            }
            return sub;
        }
	}

        /**
         * Invokes EXEHDA to remotely instantiate a new SM.
         *
         * TO DO: move this code to GridToolkit for better encapsulation
         *
         * @param smId a <code>String</code> value
         * @return a <code>SubmissionManagerRemote</code> value
         */
    private final SubmissionManagerRemote exehdaCreateNewSubmissionManager(String smId)
	{
            // O metodo setHeuristic() eh justamente o metodo que instala a heuristica
            // de escalonamento especifica do AppMan. Hoje essa heuristica desconsidera
            // aquele escalonador de proposito geral, fazendo o trabalho completo
            // sozinha. Uma melhoria do prototipo seria fazer ela interagir c/ o
            // servico Scheduler. Na epoca foi implementado dessa forma pq a integracao
            // do Scheduler ao sistema ainda nao estava concluida/estavel.
			 
        AppManUtil.getExecutor().setHeuristic(GridSchedule.getInstance());
        
        ObjectId oxID;
        try
        {    
            Debug.debug("DEBUG createNewSubmissionManagerAction TRY", true);
            
            GeneralObjectActivator gactivator = new GeneralObjectActivator(
                    "SubmissionManager",
                    new Class[] {SubmissionManagerRemote.class},
                    new String[] {"SubmissionManagerRemote"},
                    true);
					
            oxID = AppManUtil.getExecutor().createObject(
                SubmissionManager.class,
                new Object[] {smId, my_contact_address},
                gactivator,
                GridSchedule.HINT_SUBMISSION_MANAGER_NODE);
																	 
            SubmissionManagerRemote stub = (SubmissionManagerRemote)
                GeneralObjectActivator.getRemoteObjectReference(oxID,
                                                                SubmissionManagerRemote.class,
                                                                "SubmissionManagerRemote");
            
            stub.setMyObjectRemoteContactAddress(gactivator.getContactAddress(oxID,"SubmissionManagerRemote"));
            
            return stub;
        }
        catch (Exception e) {
            __debug__("ERROR: exehdaCreateNewSubmissionManager failed due to"+e);
            e.printStackTrace();
        }
        return null;
	}
}
